home *** CD-ROM | disk | FTP | other *** search
- /*
- #### # # # #
- # # # # # The FreeWare C library for
- # # ## ### # # # # ### RISC OS machines
- # # # # # # # # # # # ___________________________________
- # # #### ### ## # # # #
- # # # # # # # # # # Please refer to the accompanying
- #### ### #### # # ##### # ### documentation for conditions of use
- ________________________________________________________________________
-
- File: Mem.MidExtend.c
- Author: Copyright © 1993, 1995 Jason Williams and Jason Howat
- Version: 2.00 (18 Jun 1995)
- Purpose: Dynamic memory manager - reallocation
- */
-
- #include "Desklib:Error.h"
- #include "MemDefs.h"
-
- #include <string.h>
-
- #ifdef MEM__DEBUG
- #include <stdio.h>
- #endif
-
-
- static BOOL Mem__MidExtendNoCompact(mem_anchor *anchor, mem_header *chunk, int at, int by)
- {
- int available = chunk->realsize,
- olddatasize,
- newdatasize;
- mem_header *nextchunk = (mem_header *)((int)chunk + chunk->realsize),
- *prevchunk = (mem_header *)((int)chunk - chunk->prevrealsize),
- *newchunk = chunk;
- BOOL now_last = (chunk == mem__lastchunk);
- char *source,
- *destination;
-
- /* check space in adjoining blocks and use if available */
- if((chunk != mem__lastchunk) && (ISFREE(nextchunk)))
- {
- available += nextchunk->realsize;
- now_last = (nextchunk == mem__lastchunk);
- }
-
- if(((int)chunk != mem__heap) && (ISFREE(prevchunk)))
- {
- available += prevchunk->realsize;
- newchunk = prevchunk;
- }
-
- newdatasize = chunk->datasize + by;
- source = (char *)chunk + sizeof(mem_header);
- olddatasize = chunk->datasize;
-
- if(available >= CHUNKSIZE(chunk->datasize + by))
- {
- destination = (char *)((int)newchunk + sizeof(mem_header));
- *chunk->handle = destination;
- newchunk->handle = chunk->handle;
- newchunk->realsize = available;
- newchunk->datasize = newdatasize;
- mem__free -= WORDALIGN(newdatasize) - WORDALIGN(olddatasize);
-
- if(!now_last)
- {
- mem_header *nn = (mem_header *)((int)newchunk + available);
- nn->prevrealsize = available;
- }
-
- memmove(destination, source, at);
- memmove(destination + at + by, source + at, olddatasize - at);
-
- Mem__SplitOffFreeChunk(newchunk);
- }
- else /* try to alloc block of new size and copy old one over */
- {
- if(!Mem_Alloc((mem_anchor *)&destination, newdatasize))
- return FALSE;
-
- memmove(destination, source, at);
- memmove(destination + at + by, source + at, olddatasize - at);
-
- Mem_Free(anchor);
- *anchor = destination;
- Mem__FindChunk((mem_anchor *)&destination, &chunk);
- chunk->handle = anchor;
- }
-
- return TRUE;
- }
-
- extern BOOL Mem_MidExtend(mem_anchor *anchor, int at, int by)
- /* Enlarges or reduces a Mem chunk by moving all data beyond 'at' by
- * 'by' bytes up or down in memory. 'at' is an offset within the chunk.
- *
- * Goes to rather a lot of effort to avoid moving the base address of
- * this chunk and others, but if mem_autocompact allows, it will compact
- * if it is absolutely necessary in order to manage the extension.
- *
- * Returns TRUE if it succeeds
- */
- {
- mem_header *chunk,
- *bestfit = NULL,
- *start, *end,
- *best_start = NULL, *best_end,
- *scan_start, *scan_end,
- *end_of_heap;
- int bestfit_size,
- best_move, best_free,
- move_count, free_count,
- needed,
- newdatasize, extchunksize;
- char *source,
- *destination;
-
-
- if(by == 0) /* Zero bytes? Certainly sir! Anything else? */
- return(TRUE);
-
- if(at < 0 || !Mem__FindChunk(anchor, &chunk))
- return(FALSE);
-
- if(at > chunk->datasize)
- return(FALSE);
-
- source = (char *)chunk + sizeof(mem_header);
-
- /* ------ Reduce chunk ------ */
- if (by < 0) /* Reducing the size of the chunk */
- {
- if (-by > at) by = -at; /* Can't delete past start of chunk */
-
- mem__free += WORDALIGN(chunk->datasize) - WORDALIGN(chunk->datasize + by);
-
- memmove(source + at + by, source + at, chunk->datasize - at);
- chunk->datasize += by; /* Shrink data area of this block */
-
- Mem__SplitOffFreeChunk(chunk); /* Return any free space for use */
- /* The anchor has not changed */
- return(TRUE);
- }
-
-
- /* ------ Extend chunk ------ */
- if(mem_autocompact == mem_NOCOMPACT)
- return Mem__MidExtendNoCompact(anchor, chunk, at, by);
- else /* allowed to relocate other blocks in the search for the cheapest way
- * of creating enough space for the extension
- */
- {
- newdatasize = chunk->datasize + by;
- extchunksize = CHUNKSIZE(newdatasize);
- /* needed: is the number of free bytes required to perform the extension
- (will be a multiple of 4) */
- needed = WORDALIGN(newdatasize) - WORDALIGN(chunk->datasize);
-
- /* ensure there is enough space _before_ we go searching for the cheapest
- * way of using it
- */
- if(mem__free < needed)
- {
- /* extend the heap by at least (needed - mem__free) bytes */
- int extra = needed - mem__free,
- prevsize = mem__heapsize;
-
- mem__heapsize = Mem__HeapSize(mem__heapsize + extra + 16);
- mem__free += mem__heapsize - prevsize;
- #ifdef MEM__DEBUG
- fprintf(stderr, "grow heap: %8x\n", mem__heapsize - prevsize);
- fflush(stderr);
- #endif
- mem__lastchunk->realsize = mem__heap + mem__heapsize - (int)mem__lastchunk;
-
- if(mem__free < needed)
- {
- Mem__SplitOffFreeChunk(mem__lastchunk);
- Mem__ReduceSlot();
- return FALSE; /* unable to get enough freespace to do extension */
- }
- }
-
- start = chunk;
- end = (mem_header *)((int)chunk + chunk->realsize);
- end_of_heap = (mem_header *)(mem__heap + mem__heapsize);
-
- if(start->realsize >= extchunksize)
- {
- bestfit = start;
- bestfit_size = start->realsize;
- }
-
- move_count = start->datasize;
- free_count = start->realsize - CHUNKSIZE(start->datasize);
-
- /* Scan backwards from chunk to find start of possible run of blocks */
- while((free_count < needed) && ((int)start != mem__heap))
- {
- int used;
-
- start = (mem_header *)((int)start - start->prevrealsize);
-
- if(ISFREE(start))
- {
- used = 0;
-
- if(((bestfit == NULL) || (bestfit_size > start->realsize)) &&
- (start->realsize >= extchunksize))
- {
- bestfit = start;
- bestfit_size = bestfit_size;
- }
- }
- else
- {
- used = CHUNKSIZE(start->datasize);
- move_count += used;
- }
-
- free_count += start->realsize - used;
- }
-
- scan_start = start;
-
- /* Alternately advance end/start forwards keeping track of count of bytes
- to be moved, remembering start/end points for run of blocks with
- minimum. At the same time scan for a single free block which is big
- enough to hold the entire extended block. Check for when start == chunk
- and adjust *_count variables accordingly */
- while(1)
- {
- /* advance end and adjust *_count stats until free_count >= needed */
- while((free_count < needed) && (end < end_of_heap))
- {
- int used;
-
- if(ISFREE(end))
- {
- used = 0;
-
- if(((bestfit == NULL) || (bestfit_size > end->realsize)) &&
- (end->realsize >= extchunksize))
- {
- bestfit = end;
- bestfit_size = bestfit_size;
- }
- }
- else
- {
- used = CHUNKSIZE(end->datasize);
- move_count += used;
- }
-
- free_count += end->realsize - used;
-
- end = (mem_header *)((int)end + end->realsize);
- }
-
- /* check stats and copy to best_* if needed */
- if((free_count >= needed) &&
- ((best_start == NULL) || (best_move > move_count)))
- {
- best_start = start;
- best_end = end;
- best_move = move_count;
- best_free = free_count;
- }
-
- if(start == chunk)
- break;
-
- {
- int used;
-
- /* adjust *_count stats and advance start one block */
- if(!ISFREE(start))
- {
- used = CHUNKSIZE(start->datasize);
- move_count -= used;
- }
- else
- used = 0;
- free_count -= start->realsize - used;
- }
-
- start = (mem_header *)((int)start + start->realsize);
-
- if(start == chunk)
- move_count -= sizeof(mem_header) + at;
- }
-
- scan_end = end;
-
- /* Consider cost of moving whole block to larger free block and choose
- * cheapest option for the extension of the block.
- * NB: as we have already ensured that (mem__free >= needed), best_start
- * must be non-NULL, as a run of blocks has to exist.
- */
- if(best_move > chunk->datasize)
- {
- /* check chunks before scan_start and after scan_end for single free
- * block that is large enough for the extended block, as it is cheaper
- * to relocate target block.
- */
- mem_header *check;
-
- check = (mem_header *)mem__heap;
- while(check < scan_start)
- {
- if(ISFREE(check))
- {
- if(((bestfit == NULL) || (bestfit_size > check->realsize)) &&
- (check->realsize >= extchunksize))
- {
- bestfit = check;
- bestfit_size = check->realsize;
- }
- }
-
- check = (mem_header *)((int)check + check->realsize);
- }
-
- check = scan_end;
- while(check < end_of_heap)
- {
- if(ISFREE(check))
- {
- if(((bestfit == NULL) || (bestfit_size > check->realsize)) &&
- (check->realsize >= extchunksize))
- {
- bestfit = check;
- bestfit_size = check->realsize;
- }
- }
-
- check = (mem_header *)((int)check + check->realsize);
- }
- }
-
- if((bestfit != NULL) && (best_move > chunk->datasize))
- {
- /* it is cheaper to relocate entire chunk */
- #ifdef MEM__DEBUG
- fprintf(stderr, "relocate: bestfit %p\n", bestfit);
- fflush(stderr);
- #endif
-
- /* copy data */
- destination = (char *)bestfit + sizeof(mem_header);
- memmove(destination, source, at);
- memmove(destination + at + by, source + at, chunk->datasize - at);
-
- /* update heap control structures */
- bestfit->handle = chunk->handle;
- bestfit->datasize = newdatasize;
- Mem_Free(anchor);
- *bestfit->handle = destination;
- mem__free -= extchunksize;
-
- Mem__SplitOffFreeChunk(bestfit);
-
- return(TRUE);
- }
- else
- {
- /* perform extension by:
- * compacting region defined by best_start/best_end
- * move data to new locations
- * update headers of surrounding and target chunks
- */
- mem_header *newchunk;
- int newchunksize,
- oldchunksize;
-
- if(best_start < chunk) /* must be free space available before chunk */
- {
- Mem__ShuffleDown(best_start, chunk);
- newchunk = (mem_header *)((int)chunk - chunk->prevrealsize);
- }
- else
- newchunk = chunk;
- destination = (char *)((int)newchunk + sizeof(mem_header));
-
- if(best_end > (mem_header *)((int)chunk + chunk->realsize))
- Mem__ShuffleUp(chunk, best_end);
-
- newchunksize = chunk->realsize;
- if(best_start < chunk)
- newchunksize += newchunk->realsize;
-
- if(extchunksize > newchunksize) /* sanity check */
- Error_ReportFatalInternal(0, "Unexpected failure in Mem_MidExtend");
-
- /* update anchors/links */
- oldchunksize = chunk->datasize;
- newchunk->handle = chunk->handle;
- newchunk->datasize = newdatasize;
- newchunk->realsize = newchunksize;
- *(newchunk->handle) = destination;
-
- /* move data */
- memmove(destination, source, at);
- memmove(destination + at + by, source + at, oldchunksize - at);
-
- if(chunk != mem__lastchunk)
- {
- mem_header *next = (mem_header *)((int)newchunk + newchunksize);
- next->prevrealsize = newchunksize;
- }
- else
- mem__lastchunk = newchunk; /* update mem__lastchunk */
-
- Mem__SplitOffFreeChunk(newchunk);
- }
- }
-
- mem__free -= needed; /* successful allocation - update stats */
-
- return(TRUE);
- }
-